/*!
 * \file WtKVCache.hpp
 * \brief 高性能键值对缓存类，基于内存映射文件实现
 * 
 * \details 提供线程安全的键值对缓存功能，包括：
 *          - 基于内存映射文件的持久化存储
 *          - 高效的哈希表索引机制
 *          - 自动容量扩展和文件管理
 *          - 日期验证和数据一致性保护
 *          - 线程安全的并发访问控制
 *          
 *          特别适用于需要高频访问和持久化的缓存场景，
 *          如交易系统中的代码转换、配置缓存等。
 */
#pragma once
#include "SpinMutex.hpp"
#include "BoostFile.hpp"
#include "BoostMappingFile.hpp"

#include "../Includes/FasterDefs.h"

/// 容量扩展步长
#define SIZE_STEP 200
/// 缓存文件标识符
#define CACHE_FLAG "&^%$#@!\0"
/// 标识符长度
#define FLAG_SIZE 8

/// BoostMappingFile智能指针类型定义
typedef std::shared_ptr<BoostMappingFile> BoostMFPtr;

#pragma warning(disable:4200)

NS_WTP_BEGIN

/// 缓存日志回调函数类型
typedef std::function<void(const char*)> CacheLogger;

/*!
 * \brief 高性能键值对缓存类
 * 
 * \details 基于内存映射文件实现的线程安全键值对缓存，特点：
 *          - 持久化存储，程序重启后数据保持
 *          - 基于自旋锁的高效并发控制
 *          - 动态容量扩展，自动文件管理
 *          - 快速哈希表查找，O(1)时间复杂度
 *          - 日期验证机制，确保数据一致性
 *          
 *          内部结构：
 *          - 使用faster_hashmap提供高速索引
 *          - 采用内存映射文件进行数据持久化
 *          - 支持键值长度最大64字节
 *          - 提供完整的异常处理和日志记录
 */
class WtKVCache
{
public:
	/// 默认构造函数
	WtKVCache() {}
	
	/// 禁用拷贝构造函数
	WtKVCache(const WtKVCache&) = delete;
	
	/// 禁用赋值操作符
	WtKVCache& operator=(const WtKVCache&) = delete;

private:
	
	/*!
	 * \brief 缓存项结构体
	 * 
	 * \details 存储单个键值对的数据结构，
	 *          键和值都限制在64字节以内
	 */
	typedef struct _CacheItem
	{
		char	_key[64] = { 0 };	///< 键，最大63个字符+1个结束符
		char	_val[64] = { 0 };	///< 值，最大63个字符+1个结束符
	} CacheItem;

	/*!
	 * \brief 缓存块结构体
	 * 
	 * \details 内存映射文件的头部结构，包含缓存的元信息
	 *          和可变长度的缓存项数组
	 */
	typedef struct CacheBlock
	{
		char		_blk_flag[FLAG_SIZE];	///< 文件标识符，用于验证文件格式
		uint32_t	_size;					///< 当前已使用的缓存项数量
		uint32_t	_capacity;				///< 缓存容量（最大可存储项数）
		uint32_t	_date;					///< 日期标识，用于数据一致性验证
		CacheItem	_items[0];				///< 可变长度的缓存项数组
	} CacheBlock;

	/*!
	 * \brief 缓存块配对结构体
	 * 
	 * \details 管理缓存块指针和对应的内存映射文件
	 */
	typedef struct _CacheBlockPair
	{
		CacheBlock*		_block;		///< 缓存块指针
		BoostMFPtr		_file;		///< 内存映射文件智能指针

		/// 构造函数，初始化为空指针
		_CacheBlockPair()
		{
			_block = NULL;
			_file = NULL;
		}
	} CacheBlockPair;

	CacheBlockPair	_cache;								///< 缓存数据管理器
	SpinMutex		_lock;								///< 自旋锁，保证线程安全
	faster_hashmap<LongKey, uint32_t> _indice;		///< 哈希表索引，key->index映射

private:
	/*!
	 * \brief 调整缓存容量
	 * \param newCap 新的容量大小
	 * \param logger 日志回调函数，可选
	 * \return 成功返回true，失败返回false
	 * 
	 * \details 扩展缓存文件大小和内存映射区域：
	 *          1. 检查当前容量是否足够
	 *          2. 计算新的文件大小并扩展文件
	 *          3. 重新映射文件到内存
	 *          4. 更新缓存块的容量信息
	 *          
	 *          注意：调用此函数前应该保证线程安全
	 */
	bool	resize(uint32_t newCap, CacheLogger logger = nullptr)
	{
		if (_cache._file == NULL)
			return false;

		// 调用该函数之前，应该保证线程安全
		CacheBlock* cBlock = _cache._block;
		if (cBlock->_capacity >= newCap)
			return _cache._file->addr();

		std::string filename = _cache._file->filename();
		uint64_t uOldSize = sizeof(CacheBlock) + sizeof(CacheItem)*cBlock->_capacity;
		uint64_t uNewSize = sizeof(CacheBlock) + sizeof(CacheItem)*newCap;
		std::string data;
		data.resize((std::size_t)(uNewSize - uOldSize), 0);
		try
		{
			BoostFile f;
			f.open_existing_file(filename.c_str());
			f.seek_to_end();
			f.write_file(data.c_str(), data.size());
			f.close_file();
		}
		catch (std::exception&)
		{
			if (logger) logger("Got an exception while resizing cache file");
			return false;
		}


		_cache._file.reset();
		BoostMappingFile* pNewMf = new BoostMappingFile();
		try
		{
			if (!pNewMf->map(filename.c_str()))
			{
				delete pNewMf;
				if (logger) logger("Mapping cache file failed");
				return false;
			}
		}
		catch (std::exception&)
		{
			if (logger) logger("Got an exception while mapping cache file");
			return false;
		}

		_cache._file.reset(pNewMf);

		_cache._block = (CacheBlock*)_cache._file->addr();
		_cache._block->_capacity = newCap;
		return true;
	}

public:
	/*!
	 * \brief 初始化缓存
	 * \param filename 缓存文件路径
	 * \param uDate 日期标识，用于数据一致性验证
	 * \param logger 日志回调函数，可选
	 * \return 成功返回true，失败返回false
	 * 
	 * \details 初始化缓存系统的完整流程：
	 *          1. 检查缓存文件是否存在，不存在则创建
	 *          2. 建立内存映射，获取缓存块指针
	 *          3. 验证日期标识，不匹配则清空缓存
	 *          4. 检查文件完整性，修复损坏的元信息
	 *          5. 重建内存哈希表索引
	 *          
	 *          支持断电恢复和异常处理机制
	 */
	bool	init(const char* filename, uint32_t uDate, CacheLogger logger = nullptr)
	{
		bool isNew = false;
		if (!BoostFile::exists(filename))
		{
			uint64_t uSize = sizeof(CacheBlock) + sizeof(CacheItem) * SIZE_STEP;
			BoostFile bf;
			bf.create_new_file(filename);
			bf.truncate_file((uint32_t)uSize);
			bf.close_file();

			isNew = true;
		}

		_cache._file.reset(new BoostMappingFile);
		if (!_cache._file->map(filename))
		{
			_cache._file.reset();
			if (logger) logger("Mapping cache file failed");
			return false;
		}
		_cache._block = (CacheBlock*)_cache._file->addr();

		if (!isNew &&  _cache._block->_date != uDate)
		{
			 _cache._block->_size = 0;
			 _cache._block->_date = uDate;

			memset(& _cache._block->_items, 0, sizeof(CacheItem)* _cache._block->_capacity);

			if (logger) logger("Cache file reset due to a different date");
		}

		if (isNew)
		{
			 _cache._block->_capacity = SIZE_STEP;
			 _cache._block->_size = 0;
			 _cache._block->_date = uDate;
			strcpy( _cache._block->_blk_flag, CACHE_FLAG);
		}
		else
		{
			// 检查缓存文件是否被破坏，要自动恢复
			do
			{
				uint64_t uSize = sizeof(CacheBlock) + sizeof(CacheItem) *  _cache._block->_capacity;
				uint64_t realSz =  _cache._file->size();
				if (realSz != uSize)
				{
					uint32_t realCap = (uint32_t)((realSz - sizeof(CacheBlock)) / sizeof(CacheItem));
					uint32_t markedCap =  _cache._block->_capacity;
					// 文件大小不匹配，一般是因为capacity标记错误，实际没有问题
					// 进行一下数据检查
					 _cache._block->_capacity = realCap;
					 _cache._block->_size = (realCap < markedCap) ? realCap : markedCap;
				}

			} while (false);
		}

		// 将数据项添加到hashmap中
		for (uint32_t i = 0; i < _cache._block->_size; i++)
			_indice[_cache._block->_items[i]._key] = i;

		return true;
	}

	/*!
	 * \brief 清空缓存
	 * 
	 * \details 清空所有缓存数据：
	 *          1. 获取写锁
	 *          2. 清空哈希表索引
	 *          3. 清零缓存项内存区域
	 *          4. 重置大小计数器
	 *          5. 释放锁
	 */
	inline void clear()
	{
		_lock.lock();

		if (_cache._block == NULL)
			return;

		_indice.clear();

		memset(_cache._block->_items, 0, sizeof(CacheItem)*_cache._block->_capacity);
		_cache._block->_size = 0;

		_lock.unlock();
	}

	/*!
	 * \brief 获取键对应的值
	 * \param key 要查找的键
	 * \return 找到返回对应的值，未找到返回空字符串
	 * 
	 * \details 高效的键值查找：
	 *          1. 在哈希表中查找键的索引
	 *          2. 根据索引直接访问缓存项
	 *          3. 返回对应的值
	 *          
	 *          时间复杂度：O(1)
	 */
	inline const char*	get(const char* key) const
	{
		auto it = _indice.find(key);
		if (it == _indice.end())
			return "";

		return _cache._block->_items[it->second]._val;
	}

	/*!
	 * \brief 存储键值对
	 * \param key 键
	 * \param val 值
	 * \param len 值的长度，0表示自动计算
	 * \param logger 日志回调函数，可选
	 * 
	 * \details 键值对存储逻辑：
	 *          1. 查找键是否已存在
	 *          2. 存在则直接更新值
	 *          3. 不存在则检查容量，必要时扩展
	 *          4. 添加新的键值对并更新索引
	 *          
	 *          支持自动容量扩展和线程安全保护
	 */
	void	put(const char* key, const char*val, std::size_t len = 0, CacheLogger logger = nullptr)
	{
		auto it = _indice.find(key);
		if (it != _indice.end())
		{
			wt_strcpy(_cache._block->_items[it->second]._val, val, len);
		}
		else
		{
			_lock.lock();
			if(_cache._block->_size == _cache._block->_capacity)
				resize(_cache._block->_capacity + SIZE_STEP, logger);

			_indice[key] = _cache._block->_size;
			wt_strcpy(_cache._block->_items[_cache._block->_size]._key, key);
			wt_strcpy(_cache._block->_items[_cache._block->_size]._val, val, len);
			_cache._block->_size += 1;
			_lock.unlock();
		}
	}

	/*!
	 * \brief 检查键是否存在
	 * \param key 要检查的键
	 * \return 存在返回true，不存在返回false
	 */
	inline bool	has(const char* key) const 
	{
		return (_indice.find(key) != _indice.end());
	}

	/*!
	 * \brief 获取当前缓存项数量
	 * \return 缓存项数量
	 */
	inline uint32_t size() const
	{
		if (_cache._block == 0)
			return 0;

		return _cache._block->_size;
	}

	/*!
	 * \brief 获取缓存容量
	 * \return 缓存容量（最大可存储项数）
	 */
	inline uint32_t capacity() const
	{
		if (_cache._block == 0)
			return 0;

		return _cache._block->_capacity;
	}
};

NS_WTP_END